package com.ruoyi.web.core.config.timetask;

import cn.hutool.json.JSONUtil;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.ruoyi.common.constant.Constants;
import com.ruoyi.common.constant.RedisConstans;
import com.ruoyi.common.core.domain.HatDeviceLog;
import com.ruoyi.common.enums.WebSocketType;
import com.ruoyi.device.domain.DeviceAlarmHistory;
import com.ruoyi.device.domain.req.alarm.AlarmHistoryAddReq;
import com.ruoyi.device.domain.resp.hatdevice.HatDeviceResp;
import com.ruoyi.device.service.DeviceAlarmHistoryService;
import com.ruoyi.device.service.HatDeviceService;
import com.ruoyi.framework.websocket.WebSocketUsers;
import com.ruoyi.web.core.config.mqtt.consumer.MqttConsumerDeviceCallBack;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.scheduling.annotation.EnableScheduling;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import org.springframework.stereotype.Component;

import javax.annotation.PostConstruct;
import java.math.BigDecimal;
import java.sql.Timestamp;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.*;
import java.util.concurrent.ConcurrentLinkedQueue;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

/**
 * @Author wy
 * @Description TODO
 * @Date 2023/11/27 17:01
 * @Version 1.0
 */
@EnableScheduling
@Component
public class SendDeviceToVue {
    /**
     * 赤道半径（单位：米）
     */
    private static final double EQUATOR_RADIUS = 6378137;

    @Autowired
    private ThreadPoolTaskExecutor  threadPoolTaskExecutor;

    private ObjectMapper  objectMapper=new ObjectMapper();

    @Autowired
    private RedisTemplate redisTemplate;

    @Autowired
    private HatDeviceService hatDeviceService;

    @Autowired
    private DeviceAlarmHistoryService deviceAlarmHistoryService;

    //项目启动就将所有已经绑定的电子围栏写入redis
    @PostConstruct
    public void BingFenceToRedis(){
        List<HatDeviceResp> list = this.hatDeviceService.searchByOrgCode(Constants.CQ_CODE);
        List<HatDeviceResp> respList = list.stream().filter(p -> {
            return p.getElectricFenceResp() != null;
        }).collect(Collectors.toList());
        for (HatDeviceResp deviceResp : respList) {
            this.redisTemplate.opsForHash().put(RedisConstans.BIND_FENCE,deviceResp.getDeviceCode()+deviceResp.getElectricFenceResp().getUserName()+deviceResp.getElectricFenceResp().getId(),deviceResp);
        }
    }

    /**
     * 每隔7秒执行一次
     * cron: 秒  分   小时   年  月
     * */
    @Scheduled(cron = "0/7 * * * * ?")
    public void sendDevice() throws JsonProcessingException {
        ConcurrentLinkedQueue<HatDeviceLog> thisLinkedQueue = MqttConsumerDeviceCallBack.getThisLinkedQueue();

        //没有数据，不用做
        if(thisLinkedQueue.isEmpty()){
            return;
        }
        //没有用，也不用做
        if(WebSocketUsers.getUserNames().isEmpty()){
            return;
        }

        //复制一份拿来计算距离
        Queue<HatDeviceLog> linkedList = new LinkedList<>();
        linkedList.addAll(thisLinkedQueue);
        List<HatDeviceResp> list = this.redisTemplate.opsForHash().values(RedisConstans.BIND_FENCE);

        //循环开始
        for (HatDeviceLog hatDeviceLog : linkedList) {
            for (HatDeviceResp hatDeviceResp : list) {
                if(hatDeviceLog.getDeviceCode().equals(hatDeviceResp.getDeviceCode())){
                    BigDecimal lat = hatDeviceLog.getLat();
                    BigDecimal lon = hatDeviceLog.getLon();
                    BigDecimal latitude = hatDeviceResp.getElectricFenceResp().getLatitude();
                    BigDecimal longitude = hatDeviceResp.getElectricFenceResp().getLongitude();
                    Double radius = hatDeviceResp.getElectricFenceResp().getRadius();
                    Boolean aTure = isTure(lat.doubleValue(), lon.doubleValue(), latitude.doubleValue(), longitude.doubleValue(), radius);
                    //为true 在范围之外
                    if(aTure){
                        //看是否已经存在，报过警了
                        Object o = this.redisTemplate.opsForValue().get(RedisConstans.ALARM_DEVICE + ":" + hatDeviceResp.getDeviceCode());
                        if(Objects.isNull(o)){
                            //不存在就存入redis 10分钟过期
                            this.redisTemplate.opsForValue().set(RedisConstans.ALARM_DEVICE + ":" + hatDeviceResp.getDeviceCode(),1,10, TimeUnit.MINUTES);
                            //写入警报
                            AlarmHistoryAddReq req = new AlarmHistoryAddReq();
                            req.setAlarmTime(new Timestamp(new Date().getTime()));
                            req.setDeviceId(hatDeviceResp.getDeviceId());
                            req.setDeviceName(hatDeviceLog.getDeviceCode());
                            req.setReason("超出电子围栏");
                            req.setUserId(hatDeviceResp.getElectricFenceResp().getUserId());
                            req.setUserName(hatDeviceResp.getElectricFenceResp().getUserName());
                            req.setDeviceLat(hatDeviceLog.getLat());
                            req.setDeviceLon(hatDeviceLog.getLon());
                            req.setFenceRadius(hatDeviceResp.getElectricFenceResp().getRadius());
                            req.setFenceName(hatDeviceResp.getElectricFenceResp().getName());
                            req.setFenceLon(hatDeviceResp.getElectricFenceResp().getLongitude());
                            req.setFenceLat(hatDeviceResp.getElectricFenceResp().getLatitude());
                            req.setFenceId(hatDeviceResp.getElectricFenceResp().getId());

                            this.deviceAlarmHistoryService.add(req);
                        }
                    }else {
                        //为false,在电子围栏内或已经进入电子围栏
                        Object o = this.redisTemplate.opsForValue().get(RedisConstans.ALARM_DEVICE + ":" + hatDeviceResp.getDeviceCode());
                        //不为空就删除报警标识
                        if(!Objects.isNull(o)){
                            this.redisTemplate.delete(RedisConstans.ALARM_DEVICE + ":" + hatDeviceResp.getDeviceCode());
                        }
                    }
                }
            }
        }


        //组装消息
        Map<String,Object> dataMap=new HashMap<>();
        dataMap.put("type", WebSocketType.DEVICE_INFO.getInfo());
        String jsonStr = objectMapper.writeValueAsString(thisLinkedQueue);
        dataMap.put("data", jsonStr);
        dataMap.put("timestamp", LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")));
        //清空原始数据
        thisLinkedQueue.clear();
         //发送
        WebSocketUsers.sendMessageToUsersByText(JSONUtil.toJsonStr(dataMap));
    }

    public static Boolean isTure(double longitude1, double latitude1, double longitude2, double latitude2,double radius){
        // 纬度
        double lat1 = Math.toRadians(latitude1);
        double lat2 = Math.toRadians(latitude2);
        // 经度
        double lon1 = Math.toRadians(longitude1);
        double lon2 = Math.toRadians(longitude2);
        // 纬度之差
        double a = lat1 - lat2;
        // 经度之差
        double b = lon1 - lon2;
        // 计算两点距离的公式
        double s = 2 * Math.asin(Math.sqrt(Math.pow(Math.sin(a / 2), 2) + Math.cos(lat1) * Math.cos(lat2) * Math.pow(Math.sin(b / 2), 2)));
        // 弧长乘赤道半径, 返回单位: 米
        s = s * EQUATOR_RADIUS;
        if(s>radius){
            return true;
        }else {
            return false;
        }
    }

}
